// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/task_scheduler/scheduler_service_thread.h"

#include <utility>

#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/synchronization/waitable_event.h"
#include "base/task_scheduler/delayed_task_manager.h"
#include "base/task_scheduler/scheduler_worker.h"
#include "base/task_scheduler/sequence.h"
#include "base/threading/thread_checker.h"
#include "base/time/time.h"

namespace base {
namespace internal {
    namespace {

        class ServiceThreadDelegate : public SchedulerWorker::Delegate {
        public:
            ServiceThreadDelegate(DelayedTaskManager* delayed_task_manager)
                : delayed_task_manager_(delayed_task_manager)
            {
            }

            // SchedulerWorker::Delegate:
            void OnMainEntry(SchedulerWorker* worker) override { }

            scoped_refptr<Sequence> GetWork(SchedulerWorker* worker) override
            {
                delayed_task_manager_->PostReadyTasks();
                return nullptr;
            }

            void ReEnqueueSequence(scoped_refptr<Sequence> sequence) override
            {
                NOTREACHED() << "GetWork() never returns a sequence so there's nothing to reenqueue.";
            }

            TimeDelta GetSleepTimeout() override
            {
                const TimeTicks next_time = delayed_task_manager_->GetDelayedRunTime();
                if (next_time.is_null())
                    return TimeDelta::Max();

                // For delayed tasks with delays that are really close to each other, it is
                // possible for the current time to advance beyond the required
                // GetDelayedWaitTime. Return a minimum of TimeDelta() in the event that
                // happens.
                TimeDelta sleep_time = next_time - delayed_task_manager_->Now();
                const TimeDelta zero_delta;
                return sleep_time < zero_delta ? zero_delta : sleep_time;
            }

            bool CanDetach(SchedulerWorker* worker) override
            {
                return false;
            }

        private:
            DelayedTaskManager* const delayed_task_manager_;

            DISALLOW_COPY_AND_ASSIGN(ServiceThreadDelegate);
        };

    } // namespace

    SchedulerServiceThread::~SchedulerServiceThread() = default;

    // static
    std::unique_ptr<SchedulerServiceThread> SchedulerServiceThread::Create(
        TaskTracker* task_tracker, DelayedTaskManager* delayed_task_manager)
    {
        std::unique_ptr<SchedulerWorker> worker = SchedulerWorker::Create(
            ThreadPriority::NORMAL,
            WrapUnique(new ServiceThreadDelegate(delayed_task_manager)),
            task_tracker,
            SchedulerWorker::InitialState::ALIVE);
        if (!worker)
            return nullptr;

        return WrapUnique(new SchedulerServiceThread(std::move(worker)));
    }

    void SchedulerServiceThread::WakeUp()
    {
        worker_->WakeUp();
    }

    void SchedulerServiceThread::JoinForTesting()
    {
        worker_->JoinForTesting();
    }

    SchedulerServiceThread::SchedulerServiceThread(
        std::unique_ptr<SchedulerWorker> worker)
        : worker_(std::move(worker))
    {
    }

} // namespace internal
} // namespace base
